/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package lv.lu.gnss_accuracy_test;


import java.util.LinkedList;
import java.util.List;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;

/**
 * This the app's main Activity. It provides buttons for requesting the various features of the
 * app, displays the current location, the current address, and the status of the location client
 * and updating services.
 *
 * {@link #getLocation} gets the current location using the Location Services getLastLocation()
 * function. {@link #getAddress} calls geocoding to get a street address for the current location.
 * {@link #startUpdates} sends a request to Location Services to send periodic location updates to
 * the Activity.
 * {@link #stopUpdates} cancels previous periodic update requests.
 *
 * The update interval is hard-coded to be 5 seconds.
 */
public class MainActivity extends FragmentActivity implements
        LocationListener, OnItemSelectedListener,
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener {

    // A request to connect to Location Services
    private LocationRequest mLocationRequest;

    // Stores the current instantiation of the location client in this object
    private LocationClient mLocationClient;
    private GoogleMap map;

    // Handles to UI widgets
    private TextView mSats;
//    private TextView mAddress;
//    private ProgressBar mActivityIndicator;
//    private TextView mConnectionState;
//    private TextView mConnectionStatus;

    // Handle to SharedPreferences for this app
    SharedPreferences mPrefs;

    // Handle to a SharedPreferences editor
    SharedPreferences.Editor mEditor;

    boolean mUpdatesRequested = true;

    private Spinner spin1;
    private Spinner spin2;

    final long MAX_LOCATIONS_LIST_SIZE = 10000;
    List<Location> allLocs = new LinkedList<Location>();
    private RetainedFragment dataFragment;

	private ImageView mStatusCircle;

    /*
     * Initialize the Activity
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSats = (TextView)findViewById(R.id.label); 

//		SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
//				R.array.frequency, 
//				android.R.layout.simple_spinner_dropdown_item);
// 
	       ArrayAdapter<String> adapter1 = new ArrayAdapter<String>(
	                MainActivity.this, android.R.layout.simple_spinner_dropdown_item,
	                getResources().getStringArray(R.array.frequency) );
	       ArrayAdapter<String> adapter2 = new ArrayAdapter<String>(
	                MainActivity.this, android.R.layout.simple_dropdown_item_1line,
	                getResources().getStringArray(R.array.accuracy) );
	        final ActionBar actionBar = getActionBar();
	        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
	        actionBar.setDisplayShowTitleEnabled(false);
	        actionBar.setDisplayShowCustomEnabled(true);
	        actionBar.setDisplayUseLogoEnabled(false);
	        actionBar.setDisplayShowHomeEnabled(false);
	        actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
	        actionBar.setCustomView(R.layout.actionbar);

	        spin1 = (Spinner) findViewById(R.id.spinner1);
	        spin2 = (Spinner) findViewById(R.id.spinner2);

	        spin1.setAdapter(adapter1);
	        spin1.setSelection(1);
	        spin2.setAdapter(adapter2);
	        spin1.setOnItemSelectedListener(this);
	        spin2.setOnItemSelectedListener(this);
	            
        // Create a new global location parameters object
        mLocationRequest = LocationRequest.create();
        map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
        map.setMyLocationEnabled(true);

        mLocationRequest.setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);
        mUpdatesRequested = true;

        mPrefs = getSharedPreferences(LocationUtils.SHARED_PREFERENCES, Context.MODE_PRIVATE);
        mEditor = mPrefs.edit();

        mLocationClient = new LocationClient(this, this, this);
        mStatusCircle = (ImageView) findViewById(R.id.notification);

        // retain data on screen change 
        FragmentManager fm = getFragmentManager();
        dataFragment = (RetainedFragment) fm.findFragmentByTag("savedMapLocations");
        // create the fragment and data the first time
        if (dataFragment == null) {
            // add the fragment
            dataFragment = new RetainedFragment();
            fm.beginTransaction().add(dataFragment, "savedMapLocations").commit();
            // load the data from the web
            dataFragment.setData(allLocs);
        }
        allLocs = dataFragment.getData();
        for ( Location l : allLocs) {
        	putCircleOnMap(l);        
        }
    }
    
	@Override
	public void onItemSelected(AdapterView<?> parent, View v, int position, long id ) {
        /* Use the following switch-statement if you want to keep all spinner actions/callbacks together */
        /* The same can be done to the onNothingSelected callback */
        stopPeriodicUpdates();
		switch(parent.getId()) {
        case R.id.spinner1:
        	int interval = 10000;
        	try { 
        		interval = Integer.valueOf((String) parent.getItemAtPosition(position));
//                Log.d(LocationUtils.APPTAG, String.valueOf(interval) );
        	} catch ( NumberFormatException nfe ) {
        		stopUpdates(null);
        		return;
        	}
            mLocationRequest.setInterval(LocationUtils.MILLISECONDS_PER_SECOND * interval );
            mLocationRequest.setFastestInterval(LocationUtils.MILLISECONDS_PER_SECOND * interval );
            showToast("Refresh interval set to : " + interval );
            break;
         case R.id.spinner2:
        	 final int[] PRIORITY = { LocationRequest.PRIORITY_HIGH_ACCURACY, LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY,
        			 LocationRequest.PRIORITY_LOW_POWER, LocationRequest.PRIORITY_NO_POWER };
        	 mLocationRequest.setPriority( PRIORITY[position] );
             showToast("Priority set to : " + PRIORITY[position] );
             break;
        }
        if (mUpdatesRequested) {
        	startPeriodicUpdates();
        }
	}

	@Override
	public void onNothingSelected(AdapterView<?> arg0) {
		
	}


    /*
     * Called when the Activity is no longer visible at all.
     * Stop updates and disconnect.
     */
    @Override
    public void onStop() {

        // If the client is connected
        if (mLocationClient.isConnected()) {
            stopPeriodicUpdates();
        }

        // After disconnect() is called, the client is considered "dead".
        mLocationClient.disconnect();

        super.onStop();
    }
    /*
     * Called when the Activity is going into the background.
     * Parts of the UI may be visible, but the Activity is inactive.
     */
    @Override
    public void onPause() {

        // Save the current setting for updates
        mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, mUpdatesRequested);
        mEditor.commit();

        super.onPause();
    }

    /*
     * Called when the Activity is restarted, even before it becomes visible.
     */
    @Override
    public void onStart() {

        super.onStart();

        // work in onResume()
        mLocationClient.connect();

    }
    /*
     * Called when the system detects that this Activity is now visible.
     */
    @Override
    public void onResume() {
        super.onResume();

        // If the app already has a setting for getting location updates, get it
        if (mPrefs.contains(LocationUtils.KEY_UPDATES_REQUESTED)) {
            mUpdatesRequested = mPrefs.getBoolean(LocationUtils.KEY_UPDATES_REQUESTED, false);

        // Otherwise, turn off location updates until requested
        } else {
            mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, false);
            mEditor.commit();
        }

    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        // store the data in the fragment
        dataFragment.setData(allLocs);
    }
    /*
     * Handle results returned to this Activity by other Activities started with
     * startActivityForResult(). In particular, the method onConnectionFailed() in
     * LocationUpdateRemover and LocationUpdateRequester may call startResolutionForResult() to
     * start an Activity that handles Google Play services problems. The result of this
     * call returns here, to onActivityResult.
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {

        // Choose what to do based on the request code
        switch (requestCode) {

            // If the request code matches the code sent in onConnectionFailed
            case LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST :

                switch (resultCode) {
                    // If Google Play services resolved the problem
                    case Activity.RESULT_OK:

                        // Log the result
                        Log.d(LocationUtils.APPTAG, getString(R.string.resolved));

                        // Display the result
                        getLocation(null);
                    break;

                    // If any other result was returned by Google Play services
                    default:
                        // Log the result
                        Log.d(LocationUtils.APPTAG, getString(R.string.no_resolution));

                        // Display the result
//                        mConnectionState.setText(R.string.disconnected);
//                        mConnectionStatus.setText(R.string.no_resolution);

                    break;
                }

            // If any other request code was received
            default:
               // Report that this Activity received an unknown requestCode
               Log.d(LocationUtils.APPTAG,
                       getString(R.string.unknown_activity_request_code, requestCode));

               break;
        }
    }

    /**
     * Verify that Google Play services is available before making a request.
     *
     * @return true if Google Play services is available, otherwise false
     */
    private boolean servicesConnected() {

        // Check that Google Play services is available
        int resultCode =
                GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {
            // In debug mode, log the status
            Log.d(LocationUtils.APPTAG, getString(R.string.play_services_available));

            // Continue
            return true;
        // Google Play services was not available for some reason
        } else {
            // Display an error dialog
            Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0);
            if (dialog != null) {
                ErrorDialogFragment errorFragment = new ErrorDialogFragment();
                errorFragment.setDialog(dialog);
                errorFragment.show(getSupportFragmentManager(), LocationUtils.APPTAG);
            }
            return false;
        }
    }

    /**
     * Invoked by the "Get Location" button.
     *
     * Calls getLastLocation() to get the current location
     *
     * @param v The view object associated with this method, in this case a Button.
     */
    public void getLocation(View v) {

        // If Google Play Services is available
        if (servicesConnected()) {
            // Get the current location
        	Location currentLocation = mLocationClient.getLastLocation();
            putLocOnMap(currentLocation);
        }
    }

	/**
	 * @param currentLocation
	 */
	private void putLocOnMap(Location currentLocation) {
		
		allLocs.add(currentLocation);
		if ( allLocs.size()>MAX_LOCATIONS_LIST_SIZE ) {
			allLocs.remove( 0 ) ;
		}
		String prov = putCircleOnMap(currentLocation);
		
		Bundle bun = currentLocation.getExtras();
		if (bun!=null) {
			int sat = (Integer) bun.getInt("satellites");
			mSats.setText(String.valueOf(sat) );
		} else {
			mSats.setText( "0" );
		}
        Log.d(LocationUtils.APPTAG, LocationUtils.getLatLng(this, currentLocation) + " : "+ prov );

	}

	/**
	 * @param currentLocation
	 * @return
	 */
	private String putCircleOnMap(Location currentLocation) {
		// Display the current location on map
		LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
		map.moveCamera(CameraUpdateFactory.newLatLng(latLng));
		double radius = currentLocation.getAccuracy();
		String prov = currentLocation.getProvider();
		int fillcolor = 0x080000ee;	// semitransparent blue
		// draw circle on map - black dot in center, radius = accuracy in meters
		map.addCircle(new CircleOptions()
		.center(latLng)
		.radius(2)
		.strokeColor(Color.BLACK)
		.fillColor(Color.BLACK)
		.strokeWidth(1));
		map.addCircle(new CircleOptions()
		.center(latLng)
		.radius(radius)
		.strokeColor(Color.BLUE)
		.fillColor(fillcolor)
		.strokeWidth(1));
		return prov;
	}

    /**
     * Invoked by the "Start Updates" button
     * Sends a request to start location updates
     *
     * @param v The view object associated with this method, in this case a Button.
     */
    public void startUpdates(View v) {
        mUpdatesRequested = true;
        startPeriodicUpdates();
    }

    /**
     * Invoked by the "Stop Updates" button
     * Sends a request to remove location updates
     * request them.
     *
     * @param v The view object associated with this method, in this case a Button.
     */
    public void stopUpdates(View v) {
        mUpdatesRequested = false;
        stopPeriodicUpdates();
    }

    /**
     * Invoked by the "Clear map" button
     *
     * @param v The view object associated with this method, in this case a Button.
     */
    public void clearMap(View v) {
    	allLocs.clear();
    	map.clear();
    }

    /*
     * Called by Location Services when the request to connect the
     * client finishes successfully. At this point, you can
     * request the current location or start periodic updates
     */
    @Override
    public void onConnected(Bundle bundle) {
        
    	Location currentLocation = mLocationClient.getLastLocation();
		LatLng latLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));
		
        if (mUpdatesRequested) {
            startPeriodicUpdates();
        }
    }

    /*
     * Called by Location Services if the connection to the
     * location client drops because of an error.
     */
    @Override
    public void onDisconnected() {

    }

    /*
     * Called by Location Services if the attempt to
     * Location Services fails.
     */
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {

        /*
         * Google Play services can resolve some errors it detects.
         * If the error has a resolution, try sending an Intent to
         * start a Google Play services activity that can resolve
         * error.
         */
        if (connectionResult.hasResolution()) {
            try {

                // Start an Activity that tries to resolve the error
                connectionResult.startResolutionForResult(
                        this,
                        LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

                /*
                * Thrown if Google Play services canceled the original
                * PendingIntent
                */

            } catch (IntentSender.SendIntentException e) {

                // Log the error
                e.printStackTrace();
            }
        } else {

            // If no resolution is available, display a dialog to the user with the error.
            showErrorDialog(connectionResult.getErrorCode());
        }
    }

    /**
     * Report location updates to the UI.
     *
     * @param location The updated location.
     */
    @Override
    public void onLocationChanged(Location location) {

    	putLocOnMap(location);
    }

    /**
     * In response to a request to start updates, send a request
     * to Location Services
     */
    private void startPeriodicUpdates() {

        if ( servicesConnected() && mLocationClient.isConnected() ) {
			mLocationClient.requestLocationUpdates(mLocationRequest, this);
			mStatusCircle.setImageResource(R.drawable.circle_green);
		}
    }

    /**
     * In response to a request to stop updates, send a request to
     * Location Services
     */
    private void stopPeriodicUpdates() {
    	
        if ( servicesConnected() && mLocationClient.isConnected() ) {
        	mLocationClient.removeLocationUpdates(this);
	        mStatusCircle.setImageResource(R.drawable.circle_none);
        }
    }

    /**
     * Show a dialog returned by Google Play services for the
     * connection error code
     *
     * @param errorCode An error code returned from onConnectionFailed
     */
    private void showErrorDialog(int errorCode) {

        // Get the error dialog from Google Play services
        Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
            errorCode,
            this,
            LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);

        // If Google Play services can provide an error dialog
        if (errorDialog != null) {

            // Create a new DialogFragment in which to show the error dialog
            ErrorDialogFragment errorFragment = new ErrorDialogFragment();

            // Set the dialog in the DialogFragment
            errorFragment.setDialog(errorDialog);

            // Show the error dialog in the DialogFragment
            errorFragment.show(getSupportFragmentManager(), LocationUtils.APPTAG);
        }
    }

    /**
     * Define a DialogFragment to display the error dialog generated in
     * showErrorDialog.
     */
    public static class ErrorDialogFragment extends DialogFragment {

        // Global field to contain the error dialog
        private Dialog mDialog;

        /**
         * Default constructor. Sets the dialog field to null
         */
        public ErrorDialogFragment() {
            super();
            mDialog = null;
        }

        /**
         * Set the dialog to display
         *
         * @param dialog An error dialog
         */
        public void setDialog(Dialog dialog) {
            mDialog = dialog;
        }

        /*
         * This method must return a Dialog to the DialogFragment.
         */
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return mDialog;
        }
    }
    
    // dropdown for menu
	@SuppressLint("ValidFragment")
	public class DropDownListContentFragment extends Fragment {
		 
		String string;
 
		@Override 
		public void onAttach(Activity activity) {
			// TODO Auto-generated method stub 
			super.onAttach(activity);
			string = getTag();
		} 
 
		@Override 
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {
			// TODO Auto-generated method stub 
			TextView tv = new TextView(getActivity());
			tv.setText(string);
			return tv;
		} 
 
	}
	
	void showToast(CharSequence msg) {
	    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
	}

	
	public static class RetainedFragment extends Fragment {

	    // data object we want to retain
	    List<Location> savedLocs = new LinkedList<Location>();

	    // this method is only called once for this fragment
	    @Override
	    public void onCreate(Bundle savedInstanceState) {
	        super.onCreate(savedInstanceState);
	        // retain this fragment
	        setRetainInstance(true);
	    }

	    public void setData( List<Location> data) {
	        this.savedLocs = data;
	    }

	    public List<Location> getData() {
	        return savedLocs;
	    }
	}
}


